package com.aaa.sso.service.impl;

import com.aaa.common.constant.LoginType;
import com.aaa.common.entity.User;
import com.aaa.common.util.BaseUtil;
import com.aaa.common.util.JwtUtils;
import com.aaa.common.util.Result;
import com.aaa.sso.service.AuthService;
import com.aaa.sso.service.RemoteUserService;
import com.aaa.sso.shiro.CustomUsernamePasswordToken;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ FileName: UserAuthServiceImpl
 * @ Description:
 * @ Author:  STB
 * @ CreateTime: 2025/9/8 0008 10:03
 * @ Version: 1.0.0
 */
@Service("userAuthService")
@Log4j2
public class UserAuthServiceImpl implements AuthService {
    @Resource
    private RemoteUserService userService;

    //依赖注入redis模版
    @Resource
    private RedisTemplate redisTemplate;

    private static final String USER_NAME_KEY = "userName";

    @Override
    public Result authentication(String userName, String password) {
        //收集用户信息   LoginType.USER.toString()="User"
        CustomUsernamePasswordToken customUsernamePasswordToken =
                new CustomUsernamePasswordToken(userName, password, LoginType.USER.toString());
        //获取subject
        Subject currentUser = SecurityUtils.getSubject();
        try {
            //登录
            currentUser.login(customUsernamePasswordToken);
            //获取当前登录的用户信息
            User user = (User) currentUser.getPrincipal();
            log.info("登录成功！当前登录用户信息："+userName);
            //实例化载荷
            Map<String, String> payLoadMap = new HashMap<>();
            //可以在载荷中放入任意信息，敏感信息不要存放
            payLoadMap.put(USER_NAME_KEY, userName);
            payLoadMap.put("phoneNum", user.getPhonenumber());
            payLoadMap.put("userId", user.getUserId().toString());
            //生成token
            String token = null;
            try {
                token = JwtUtils.generateToken(payLoadMap);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            //设置放入的数据格式 为stringRedisSerializer  把放入key和value都序列化成原始的字符串  放入的是什么，看到就是什么
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            redisTemplate.setKeySerializer(stringRedisSerializer);
            //获取当前用户在redis中所有token 并删除   keys * 获取所有的   keys  admin-* 获取admin-开头的
            Set keys = redisTemplate.keys(userName + "-*");
            //循环删除
            for (Object key : keys) {
                //设置立即过期  比一个一个删除效率高
                redisTemplate.expire(key, 0, TimeUnit.SECONDS);
            }
            //存入到nosql    redis
            //参数1：key=admin-token
            //参数2： value = 当前登录的用户信息，方便后面使用时，不用去数据库查询，直接从redis获取，速度会快
            //参数3： 过期时间
            //参数4： 过期时间单位
            // 在企业实战过程中，一个token不能长时间使用，有被窃取冒用的风险，要设置过期时间 根据项目风险程度来确定过时间  高风险项目（财务，电商。。。涉及金钱类的）token过时间时间一般15-30分钟   后面随着风险 降低时间可以主逐渐增长
            redisTemplate.opsForValue().set(userName + "-" + token, user, 30, TimeUnit.MINUTES);
            //token要返回
            InetAddress address = InetAddress.getLocalHost();
            user.setLoginIp(address.getHostAddress());
            user.setLoginDate(new Date());
            userService.edit(user);
            return BaseUtil.success(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return BaseUtil.fail("用户名或者密码错误！");
    }

    @Override
    public boolean checkToken(String token) {
        // 添加日志记录器
        Logger logger = LoggerFactory.getLogger(this.getClass());

        // 打印原始token
        logger.info("开始验证token，原始token: {}", token);

        // 从token获取载荷中的userName
        String userName = "";
        try {

            userName = JwtUtils.getPayloadByToken(token).get(USER_NAME_KEY).asString().replace("\"", "");

            logger.info("处理后的userName: {}", userName);
        } catch (Exception e) {
            logger.error("解析userName过程发生异常", e);
            return false;
        }

        // 生成userKey
        String userKey = userName + "-" + token;
        logger.info("生成的userKey: {}", userKey);

        // 打印Redis序列化器配置
        logger.info("Redis key序列化器: {}", redisTemplate.getKeySerializer().getClass().getSimpleName());
        logger.info("Redis value序列化器: {}", redisTemplate.getValueSerializer().getClass().getSimpleName());

        // 验证JWT
        boolean jwtValid = false;
        try {
            jwtValid = JwtUtils.verify(token);
            logger.info("JWT验证结果: {}", jwtValid);
        } catch (Exception e) {
            logger.error("JWT验证过程发生异常", e);
            return false;
        }

        // 检查Redis中是否存在key
        boolean redisHasKey = false;
        try {
            redisHasKey = redisTemplate.hasKey(userKey);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            logger.info("Redis中是否存在key[{}]: {}", userKey, redisHasKey);
        } catch (Exception e) {
            logger.error("Redis检查key存在性发生异常", e);
            return false;
        }

        // 主验证逻辑
        if (jwtValid && redisHasKey) {
            try {
                // 检查过期时间
                Long expireSeconds = redisTemplate.getExpire(userKey);
                logger.info("key[{}]的剩余过期时间(秒): {}", userKey, expireSeconds);

                if (expireSeconds != null && expireSeconds < 300) {
                    // 获取用户信息
                    Object userObj = redisTemplate.opsForValue().get(userKey);
                    logger.info("从Redis中获取的用户信息类型: {}", userObj != null ? userObj.getClass().getName() : "null");

                    if (userObj instanceof User) {
                        User user = (User) userObj;
                        redisTemplate.opsForValue().set(userKey, user, 30, TimeUnit.MINUTES);
                        logger.info("key[{}]已续期30分钟", userKey);
                    } else {
                        logger.error("Redis中存储的不是User类型对象，实际类型: {}", userObj != null ? userObj.getClass().getName() : "null");
                    }
                }
                logger.info("token验证成功");
                return true;
            } catch (Exception e) {
                logger.error("token验证成功后的续期操作发生异常", e);
                return false;
            }
        }

        logger.info("token验证失败，JWT有效: {}, Redis存在key: {}", jwtValid, redisHasKey);
        return false;
    }
}
